package com.wdb007.venus.svc.biz.utils;

import java.util.HashMap;
import java.util.Map;
import java.util.zip.CRC32;

import com.alibaba.fastjson.JSON;

public class WdbEncrypt {
	private final static String mEncryptKey = "wdb007";

	public static void main(String args[]) throws WdbEncryptException {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("active_time", "2017-08-24 14:49:04");
		map.put("app_version", "app_version");
		map.put("os", "iOS_10.3.2");
		map.put("idfa", "13B889CB-AE02-4C31-B098-E13F9BE59CB8");
		map.put("package_name", "com.wordbang.iOSAPP");
		map.put("pushstatus", 2);
		map.put("device_type", 1);
		map.put("device", "iPhone9,1");
		map.put("uuid", "978a9804e730c924aa4fe8b17bfd50bc");
//		map.put("network", "WIFI");
		String data = JSON.toJSONString(map);
		String encryptedData = encrypt(data);
		//客户端传递到后台的就是encryptedData
		//服务端使用decrypt 解密
		String decryptedData = decrypt(encryptedData);

		System.out.println("encryptedData:" + encryptedData);
		System.out.println("decryptedData:" + decryptedData);
	}

	/**
	 * 解密十六进制密文，解密失败抛出 WdbEncryptException 异常
	 * 
	 * @param input
	 * @return
	 * @throws WdbEncryptException
	 */
	public static String decrypt(String input) throws WdbEncryptException {
		try {
			return new String(decrypt(HexBytes.hexToBytes(input)), "UTF-8");
		} catch (Throwable e) {
			if (e instanceof WdbEncryptException) {
				throw (WdbEncryptException) e;
			} else {
				throw new WdbEncryptException(e);
			}
		}
	}

	/**
	 * 解密字节数组，解密失败抛出 WdbEncryptException 异常
	 * 
	 * @param input
	 * @return
	 * @throws WdbEncryptException
	 */
	public static byte[] decrypt(byte[] input) throws WdbEncryptException {
		try {
			byte[] keyBytes = mEncryptKey.getBytes("UTF-8");

			WdbDataStruct ds = new WdbDataStruct();

			ds.encryptedDataLength = byteArrayToInt(input, 0);
			int realEncryptedDataLength = ds.encryptedDataLength ^ 0x12345678;
			if (realEncryptedDataLength < 1 || (realEncryptedDataLength + 12) != input.length) {
				throw new WdbEncryptException("长度校验失败!");
			}

			ds.crcBeforeEncrypted = byteArrayToInt(input, 4) ^ ds.encryptedDataLength;
			ds.crcAfterEncrypted = byteArrayToInt(input, 8) ^ ds.encryptedDataLength;

			byte[] encryptedData = new byte[input.length - 12];
			System.arraycopy(input, 12, encryptedData, 0, input.length - 12);

			if (ds.crcAfterEncrypted != calculateCrc32(encryptedData)) {
				throw new WdbEncryptException("密文校验失败!");
			}

			byte[] decryptedData = Xxtea.decrypt(encryptedData, keyBytes);
			if (ds.crcBeforeEncrypted != calculateCrc32(decryptedData)) {
				throw new WdbEncryptException("解密内容校验失败!");
			}

			return decryptedData;
		} catch (Throwable e) {
			if (e instanceof WdbEncryptException) {
				throw (WdbEncryptException) e;
			} else {
				throw new WdbEncryptException(e);
			}
		}
	}

	/**
	 * 加密字符串为十六进制密文，加密失败则抛出 WdbEncryptException 异常
	 * 
	 * @param input
	 * @return
	 * @throws WdbEncryptException
	 */
	public static String encrypt(String input) throws WdbEncryptException {
		try {
			return HexBytes.byte2hex(encrypt(input.getBytes("UTF-8")));
		} catch (Throwable e) {
			if (e instanceof WdbEncryptException) {
				throw (WdbEncryptException) e;
			} else {
				throw new WdbEncryptException(e);
			}
		}
	}

	/**
	 * 对字节数组加密，加密失败则抛出 WdbEncryptException 异常
	 * 
	 * @param input
	 * @return
	 * @throws WdbEncryptException
	 */
	public static byte[] encrypt(byte[] input) throws WdbEncryptException {
		try {
			byte[] keyBytes = mEncryptKey.getBytes("UTF-8");

			WdbDataStruct ds = new WdbDataStruct();

			ds.encryptedData = Xxtea.encrypt(input, keyBytes);
			ds.encryptedDataLength = ds.encryptedData.length ^ 0x12345678;
			ds.crcBeforeEncrypted = calculateCrc32(input) ^ ds.encryptedDataLength;
			ds.crcAfterEncrypted = calculateCrc32(ds.encryptedData) ^ ds.encryptedDataLength;

			byte[] output = new byte[ds.encryptedData.length + 12];

			System.arraycopy(intToByteArray(ds.encryptedDataLength), 0, output, 0, 4);
			System.arraycopy(intToByteArray(ds.crcBeforeEncrypted), 0, output, 4, 4);
			System.arraycopy(intToByteArray(ds.crcAfterEncrypted), 0, output, 8, 4);
			System.arraycopy(ds.encryptedData, 0, output, 12, ds.encryptedData.length);

			return output;
		} catch (Throwable e) {
			throw new WdbEncryptException(e);
		}
	}

	/**
	 * 计算CRC32校验值
	 * 
	 * @param input
	 * @return
	 */
	public static int calculateCrc32(byte[] input) {
		CRC32 crc32 = new CRC32();
		crc32.update(input);
		return (int) crc32.getValue();
	}

	/**
	 * 字节数组转int数
	 * 
	 * @param b
	 * @return
	 */
	public static int byteArrayToInt(byte[] b) {
		return byteArrayToInt(b, 0);
	}

	/**
	 * 指定偏移量的字节数组转int数
	 * 
	 * @param b
	 * @param offset
	 * @return
	 */
	public static int byteArrayToInt(byte[] b, int offset) {
		return b[offset + 3] & 0xFF | (b[offset + 2] & 0xFF) << 8 | (b[offset + 1] & 0xFF) << 16
				| (b[offset + 0] & 0xFF) << 24;
	}

	/**
	 * int数转字节数组
	 * 
	 * @param a
	 * @return
	 */
	public static byte[] intToByteArray(int a) {
		return new byte[] { (byte) ((a >> 24) & 0xFF), (byte) ((a >> 16) & 0xFF), (byte) ((a >> 8) & 0xFF),
				(byte) (a & 0xFF) };
	}

	/**
	 * 加密数据中间结构
	 */
	private static class WdbDataStruct {
		public int encryptedDataLength;
		public int crcBeforeEncrypted;
		public int crcAfterEncrypted;
		public byte[] encryptedData;
	}
}
